/*** Class Name: IFulfillQueryRequestRoundRobin
 * Author: Accenture
 * Date: 3-NOV-2011
 * Requirement/Project Name: iFulfill
 * Requirement Description: To impl round robin for QueryRequest Records
 **/
public  class IFulfillQueryRequestRoundRobin
{
    class userInfoClass
    {
        List<Assignment_Groups__c> lag;
        Integer idx;
    }
        
    public static void executeRoundRobin(List<Query_Request__c> queryRequestList , Boolean UpdateEvent , Boolean insertEvent,
                 Map < ID, Query_Request__c> queryRequestMap)
    {
    
        Set<Id> sGroupIds = new Set<Id>();
        Set<Id> queueIds = new Set<Id>();
        Set<Id> qrIds= new Set<Id>();

        List<Assignment_Groups__c> lagroup = new List<Assignment_Groups__c>();
        List<   Query_Request__c> updateqrList = new List<    Query_Request__c>();
        List<   Query_Request__c> qrList = null; 
        List<Assignment_Groups__c> lAG = null;                                                                         
        List<Assignment_Group_Name__c> assignmentGroup = null;                                                               
        
        // Stores the Assignment_Group_Name__c into the map assignmentGroupMap  
        Map<ID, Assignment_Group_Name__c> assignmentGroupMap  = new Map<ID, Assignment_Group_Name__c> ();
        Map<Id, userInfoClass> mGroupMember = new Map<Id, userInfoClass>();
        // Stores the Assignment_Groups__c into the map mUpdateAG 
        Map<Id, Assignment_Groups__c> mUpdateAG = new Map<Id, Assignment_Groups__c>();
       // Maps the queue ID with Assignment group
        Map<ID, ID> mAGQIdbyQueueId = getmAGQIdbyQueueId();
        
        for(Query_Request__c  c : queryRequestList)
        {
             // If the owner changes
            if( insertEvent || (UpdateEvent && c.OwnerId != queryRequestMap.get(c.Id).OwnerId))
            {
                // See if the owner (i.e. queue) is linked to an assignment group
                         
                if(mAGQIdbyQueueId.containsKey(c.OwnerId))
                {
                    Id gId = mAGQIdbyQueueId.get(c.OwnerId);
                    sGroupIds.add(gId);
                    queueIds.add(c.OwnerId);
                    qrIds.add(c.Id);
                }
            }
        }
     if(!qrIds.isEmpty())
        {
            qrList = getOwnerIDOfQueryRequest(qrIds);
            lAG = getAssignmentGroupsMembers(sGroupIds);                                                                         
            assignmentGroup = getAssignmentGroup(sGroupIds);                                                               
           
            for(Assignment_Group_Name__c obj : assignmentGroup )
            {    
               assignmentGroupMap.put(obj.id, obj ) ;
            }                                                                 
            for(Id gId : sGroupIds)
            {
                for(Assignment_Groups__c ag : lAG)
                {
                    if(ag.Group_Name__c == gId)
                    {
                        lagroup.add(ag);
                    }  
                }
                if(!lagroup.isEmpty())
                {   
                    userInfoClass u = new userInfoClass();
                    u.lag = lagroup;
                    u.idx = 0;
                    for(ID qId : queueIds)
                    {   
                        if(mAGQIdbyQueueId.get(qId) == gId)
                        {   
                            mGroupMember.put(qId, u);
                        }
                    }
                }
            }
            for(    Query_Request__c c : qrList)
            {

                if(mGroupMember.containsKey(c.OwnerId))
                {
                    userInfoClass u_item = mGroupMember.get(c.OwnerId);
                    Assignment_Groups__c ag_item = u_item.lag[u_item.idx];
                    Assignment_Group_Name__c assignGroupObj  = assignmentGroupMap.get(ag_item.Group_Name__c);
            
                    //Check the assignment group is part of round robin
                    if(assignGroupObj!= null && assignGroupObj.Round_Robin__c)
                    {
                        c.OwnerId = ag_item.User__c;
                        updateqrList.add(c); // add into case update list
                    }
                    //Check if  assignment goroup is associated with the queue and is  part of round robin
                    if(!mUpdateAG.containsKey(ag_item.Id) && assignGroupObj!= null && assignGroupObj.Round_Robin__c)
                    {
                        datetime now = datetime.now();
                        ag_item.Last_Assignment__c = now;
                        ag_item.Millisecond__c = now.millisecondGMT();
                        mUpdateAG.put(ag_item.Id, ag_item);
                    }
                 
                    u_item.idx++;
                    u_item.idx = math.mod(u_item.idx, u_item.lag.size());
                }
            }
       
            try 
            {
                if(updateqrList.size() > 0)
                {
                    update updateqrList; // OwnerId must be updated again after insert/update
                }
                if(mUpdateAG.values().size() >0)
                {
                    update mUpdateAG.values(); // update Assignment Time for Assignment Group Member
                }   
            }
            catch (Exception ex)
            {
                GeneralConstants.mailAnyExceptionToMe('IFulfillQueryRequestRoundRobin','executeRoundRobin',ex);
                System.debug('Exception in update the Case and Assignment Group Member: ' + ex);
            }
        }
    }
    /*
     *  Returns the Query Request
     *
     **/
    public static List< Query_Request__c> getOwnerIDOfQueryRequest( Set<Id> qrIds)
    {
        return [select Id, OwnerId from     Query_Request__c where Id IN: qrIds];   
    }
    /*
     *  Returns the users of the Assignment group
     *
     **/
    public static List<Assignment_Groups__c> getAssignmentGroupsMembers( Set<Id> sGroupIds)
    {
        
        return [select a.Id, a.Name, a.Group_Name__c, a.User__c from Assignment_Groups__c a
                                                                         where  a.Active__c =: 'true' 
                                                                         and a.User_Active__c =: 'true' 
                                                                         and a.Added_to_Queue__c = true

                                                                     //    and a.Round_Robin__c = true 
                                                                         and a.Group_Name__c IN: sGroupIds                                                                         
                                                                         ORDER BY Last_Assignment__c, Millisecond__c];
    } 
     /*
     *  Returns  the Assignment group
     *
     **/  
    public static List<Assignment_Group_Name__c> getAssignmentGroup(Set<Id> sGroupIds)
    {
       return [Select Id ,a.Name, a.OwnerId, a.Round_Robin__c from Assignment_Group_Name__c a where id in :sGroupIds];                                                                  
    }
    
    /*
     *  Returns  queue associated the Assignment group
     *
     **/  
    
    public static Map<ID, ID> getmAGQIdbyQueueId(){
    
        Map<ID, ID> mAGQIdbyQueueId = null;
        if(mAGQIdbyQueueId == null){
            mAGQIdbyQueueId = new Map<ID, ID>();
            for(Assignment_Group_Queues__c g : [select a.Assignment_Group_Name__c, a.Id, a.Name, a.QueueId__c  
                                                from Assignment_Group_Queues__c a 
                                                where a.Valid_Queue__c  =: true and a.Active__c =: 'true' and Assignment_Group_Name__r.Type__c = :IfullfillTextConstant.ASSIGNMENT_GROUP_QUERY_TYPE ]){
                mAGQIdbyQueueId.put(g.QueueId__c, g.Assignment_Group_Name__c);      
            }
        }      
        return mAGQIdbyQueueId;
    } 
}